import type { Logger, MilkioContext, createMilkioApp } from ".";
import type { failCode } from "../../src/fail-code";

export type Override<P, S> = Omit<P, keyof S> & S;

export type Mixin<T, U> = U & Omit<T, keyof U>;

export type GeneratorGeneric<T> = T extends AsyncGenerator<infer I> ? I : never;

export type MilkioApp = Awaited<ReturnType<typeof createMilkioApp>>;

export type ExecuteId = string | "global";

export type FailEnumerates = typeof failCode;

export type HttpRequest = Request;

export type HttpResponse = Override<ResponseInit & { body: string | null | undefined }, { headers: NonNullable<ResponseInit["headers"]> }>;

export type Fail<FailCode extends keyof FailEnumerates> = {
	code: FailCode;
	message: string;
	data: Parameters<FailEnumerates[FailCode]>[0];
};

export type MilkioMeta = {
	//
};

export type Cookbook = Record<string, CookbookItem>;

export type CookbookItem = {
	title?: string;
	desc?: string;
	params: string;
	cases: Array<{
		name: string;
		handler: string;
	}>;
};

export type MilkioConfig = {
	generate?: {
		significant?: Array<string>;
		insignificant?: Array<string>;
	};
	exists?: Record<string, { path: string; content: string }>;
	menubar?: {
		commands?: Array<{ name?: string; script?: string; icon?: string }>;
	};
};

export type ExecuteResult<Result> = ExecuteResultSuccess<Result> | ExecuteResultFail;

export type ExecuteStreamResult<Path, Result> = {
	getResult: () => ExecuteResultSuccess<Result> | ExecuteResultFail,
	stream: AsyncGenerator<MilkioEvent<Path>>
};

export type ExecuteResultSuccess<Result> = {
	executeId: ExecuteId;
	success: true;
	data: Result;
};

export type ExecuteResultFail<FailT extends Fail<keyof FailEnumerates> = Fail<keyof FailEnumerates>> = {
	executeId: ExecuteId;
	success: false;
	fail: FailT;
};

export type ExecuteOptions = {
	/**
	 * The executeId of the request
	 * executeId may be generated by the serverless provider, if not, a random string will be generated instead
	 */
	executeId?: string;
	/**
	 * Additional information about the request
	 * These are usually only fully implemented when called by an Http server
	 * During testing or when calling between microservices, some or all of the values may be undefined
	 */
	detail?: MilkioContext["detail"];
};

export type ExecuteCoreOptions = Mixin<
	ExecuteOptions,
	{
		executeId: string;
		logger: Logger;
		onAfterHeaders?: (headers: Headers) => void | Promise<void>;
	}
>;

export type MilkioEvent<Result> = Awaited<GeneratorGeneric<ExecuteResultSuccess<Result>["data"] extends { $type: any } ? ExecuteResultSuccess<Result>["data"]["$type"] : never>>;
